fix(scripts): doc-authoring Rule 3 reaches a GENERATED guidance table - #13283
Open
os-elon wants to merge 2 commits into
Open
fix(scripts): doc-authoring Rule 3 reaches a GENERATED guidance table#13283os-elon wants to merge 2 commits into
os-elon wants to merge 2 commits into
Conversation
A guidance table does not have to be written as an object literal. Two in
packages/spec are BUILT -- one prescription filed under each of a list of keys
via `Object.fromEntries(keys.map((k) => [k, '...']))`:
- SEPARATOR_NAV_ITEM_GUIDANCE (ui/app.zod.ts), spread into the `guidance` of
the per-variant nav options table;
- the container-key prescription at `guidance:` in ui/view.zod.ts, generated
inline at the key itself.
Both are printed verbatim at a refusing author and both sat outside the rule's
population entirely: the climb died on the unrecognised `map` / `fromEntries`
call between the callback and its sink, so the fourth population's factory
clause -- which teaches the climb to leave a function that BUILDS an options
record -- never got the chance to fire.
Measured by planting a distinct id in every literal of the nav options table
and the five tables it references: 48 of 49 candidate ids red, and the whole of
SEPARATOR_NAV_ITEM_GUIDANCE (four prose literals) silent.
`fromEntries` / `map` / `flatMap` join TRANSPARENT_CALLS. Transparency only lets
the climb CONTINUE -- it must still terminate at a recognised position -- so a
`.map()` in an ordinary helper stays silent, pinned as a precision case beside
the two new red/green pairs.
Population +19 strings (strictObject 3349 -> 3364, functionBuilt 217 -> 221),
0 new violations: none of the newly reached prose carries an internal id.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CPrUz21stTFhJRUirdc4yw
os-elon
marked this pull request as ready for review
August 30, 2026 06:15
os-elon
enabled auto-merge
August 30, 2026 06:15
os-elon
disabled auto-merge
August 30, 2026 12:13
os-elon
enabled auto-merge
August 30, 2026 12:13
os-zhuang
approved these changes
Aug 30, 2026
os-zhuang
disabled auto-merge
August 30, 2026 13:29
os-zhuang
enabled auto-merge
August 30, 2026 13:29
os-zhuang
disabled auto-merge
August 30, 2026 13:29
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #13172
The card's named instance was already discharged before this round
#13172 measured that
navItemSurfaceinpackages/spec/src/ui/app.zod.ts— anarrow function whose return type is
StrictObjectOptions— was outside Rule 3'spopulation entirely. That was true when it was filed (07:20Z). PR #13151 landed
at 13:21Z the same day with the "fourth population" clause, which teaches the
climb to leave a function that BUILDS an options record, and it closed exactly
that shape.
Re-measured on the live tree by planting a distinct id in every one of the 62
string literals of that options table and the five tables it references, then
reading which ones the gate reports:
navItemSurface(the factory itself)NAV_ITEM_ALIASESNAV_EXPANDED_ALIASES_ON_GROUPNAV_EXPANDED_ALIASES_ELSEWHERESEPARATOR_NAV_ITEM_ALIASESSEPARATOR_NAV_ITEM_GUIDANCEThe one unreached literal inside the factory is
extraKeys: ['children'], whichis a key NAME and is deliberately excluded from
STRICT_OPTION_KEYS.The card's own count re-derives cleanly: the factory holds exactly 21
ts.StringLiteralnodes (23 literals minus the two template-literal parts ofsurface), and the gate now attributes exactly 21 customer-facing strings to it(
functionBuilt 21). But only 13 of the 23 are refusal prose — the other 10 arenine variant comparison operands and the one
extraKeyskey name, none of whichshould ever be judged as prose.
What was still live: the GENERATED table
SEPARATOR_NAV_ITEM_GUIDANCEis not written as an object literal. It is BUILT —one prescription filed under each of a list of keys:
Its four prose literals are spread into the
guidanceof the very options table#13172 names, printed verbatim at a refusing author, and they were outside the
population entirely. A second instance is live in
packages/spec/src/ui/view.zod.ts,generated inline at the
guidance:key itself.The mechanism is the card's own defect one shape over. The climb reaches the
.map()callback boundary, askscustomerTextPositionabout the arrow function— the one-sentence rule the fourth population rests on — and the answer is
undefined, because the callback's parent is amapcall and its grandparent afromEntriescall, neither of which the climb recognised. The factory clausenever got the chance to fire.
The change
fromEntries,mapandflatMapjoinTRANSPARENT_CALLS. Transparency onlylets the climb CONTINUE — it must still terminate at a recognised position
(
message:/error:,.describe(), a tombstone argument, aSTRICT_OPTION_KEYSkey under astrictObjectcall, or acollectTextSinkConstssink). A
.map()in an ordinary helper reaches none of those and stays silent.What it buys is that a table's SPELLING — literal or generated — stops deciding
whether its prose is judged.
Three self-test cases in the same edit, as this file requires of any newly
recognised spelling: the hoisted generated table reaching an options factory
(red/green pair), the same generation written inline at the
guidance:key(red/green pair), and the precision case the widening owes — a generated VALUE
table reaching no sink stays unswept.
Measured
strictObject 3349 -> 3364,functionBuilt 217 -> 221.So the card's open severity question — whether the blind spot was hiding an
actual Rule 3 violation today, or only the capacity to hide one — measures as
the second: capacity only, in both instances.
extraKeyskey name above.by grep counts and blob hash, restore proven byte-identical against the HEAD
blob): reverting
TRANSPARENT_CALLSto['freeze']fails exactly the four newred assertions and returns the population to
13801 / strictObject 3349 / functionBuilt 217, so the +19 is attributable to this change alone. No rebuildleg applies —
scripts/check-doc-authoring.mjsis executed from source by nodeand has no
dist.Local checks, at
e89f70ac69Green:
check:doc-authoring(and its--self-test),check:agent-test-spelling,check:bash32-floor,check:cli-command-ids,check:cross-package-test-inputs,check:entry-guard,check:parse-guard,check:pnpm-filter-targets,check:watch-hint-literal,check:nul-bytes,check-ci-filter-parity.mjs,check-cross-package-test-inputs.mjs,check-shard-attestation.mjs, plus the twogate-script convention obligations
bare-root-worklist.mjs --self-testandcheck:pm-dispatch-gates.check-test-completeness.mjsexits 3, PREREQUISITE NOT MET — it grades a savedturbo run testlog and none exists locally. Recorded as NOT MEASURED, not as ared, exactly as its own text prescribes.
ESLint was run narrowed to the one changed file rather than repo-wide, and the
narrowing is a measurement: eslint's own config resolution puts the file in the
population,
--format jsonreports 1 file with 0 errors and 0 warnings, andeslint.config.mjsnever enables type-aware linting for any file("no
parserOptions.project, no typed@typescript-eslintrules"), so aone-file diff cannot move the verdict of any untouched file.
Scope
No changeset: this touches
scripts/only and publishes nothing — the sameshape as #13171, which changed this file alone and carried none.
skip-changesetapplies.
No contract accept/reject behaviour changes and the published surface is
unchanged:
packages/specis not edited, and this gate reads sources withoutaltering any schema. The only behaviour change is which strings a CI gate reads.
The self-test assertion-count floor raised in #13173 is out of scope here and is
not addressed by this PR; that card stays open. Nothing in this change makes it
harder — the two new red/green pairs are ordinary registrations that any
file-level floor would count.
Generated by Claude Code
Generated by Claude Code